/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package xxx.smartcardio.engine;

import java.util.logging.Level;
import java.util.logging.Logger;
import javax.smartcardio.CardException;
import javax.smartcardio.CardTerminal;
import javax.swing.SwingUtilities;

/**
 * Monitor a CardTerminal for card insertion or removal. Notifies runRemove,
 * runInsert or runChange if set on corresponding events.
 * @author Michael
 */
public class TerminalCardMonitor {
    
    private final CardTerminal term;
    private Thread thread;
    private boolean disposed = false;
    private Runnable runInsert=null;
    private Runnable runRemove=null;
    private Runnable runChange=null;

    /**
     * Get Runnable to call on card insert event.
     * @return runnable
     */
    public Runnable getRunInsert() {
        return runInsert;
    }

    /**
     * Set runable to call on card insert event. Run method is called GUI safe.
     * @param runInsert Runnable to call.
     */
    public void setRunInsert(Runnable runInsert) {
        this.runInsert = runInsert;
    }

    /**
     * Get Runnable to call on card remove event.
     * @return runnable
     */
    public Runnable getRunRemove() {
        return runRemove;
    }

    /**
     * Set runable to call on card remove event. Run method is called GUI safe.
     * @param runRemove Runnable to call.
     */
    public void setRunRemove(Runnable runRemove) {
        this.runRemove = runRemove;
    }

    /**
     * Get Runnable to call on card insert and remove events.
     * @return runnable
     */
    public Runnable getRunChange() {
        return runChange;
    }

    /**
     * Set runable to call on card remove or insert event.
     * Run method is called GUI safe.
     * @param runChange Runnable to call.
     */
    public void setRunChange(Runnable runChange) {
        this.runChange = runChange;
    }

    /**
     * Constructor
     * @param term Terminal to monitor 
     */
    public TerminalCardMonitor(CardTerminal term) {
        if (null==term) throw new NullPointerException();
        this.term = term;
        createMonitor();
        thread.setDaemon(true);
    }
    
    /**
     * Start monitor
     */
    public void start()
    {
        thread.start();
    }

    /**
     * Halt and dispose the monitor. No events will be generated after calling
     * this.
     */
    public void dispose() {
        disposed = true;
    }

    @Override
    @SuppressWarnings("FinalizeDeclaration")
    protected void finalize() throws Throwable {
        dispose();
        super.finalize();
    }

    /*
     * Create a thread to call term.waitFor.. and call corresponding Runnable
     * for corresponding events GUI save.
     */
    private void createMonitor() {
        thread = new Thread(new Runnable() {
            @Override
            @SuppressWarnings("SleepWhileInLoop")
            public void run() {
                boolean present=false;
                while (!disposed) {
                    Runnable r;
                    try {
                        boolean result;
                        if (present) result=term.waitForCardAbsent(1000);
                        else result=term.waitForCardPresent(1000);
                        if (result && !disposed) {
                            present=!present;
                            r=(present ? runInsert : runRemove);
                            if (null!=r) SwingUtilities.invokeLater(r);
                            r=runChange;
                            if (null!=r) SwingUtilities.invokeLater(r);
                        }
                    } catch (CardException ex) {
                        Logger.getLogger(TerminalCardMonitor.class.getName()).log(Level.SEVERE, null, ex);
                        try {
                            if (!disposed) Thread.sleep(250);
                        } catch (InterruptedException ex1) {
                            // ignore
                        }
                    }
                }
            }
        });  
    }
}
